package com.jrx.anytxn.transaction.service.impl;

import com.alibaba.fastjson.JSON;
import com.jrx.anytxn.common.constant.CommonContants;
import com.jrx.anytxn.common.data.TxnRespCode;
import com.jrx.anytxn.common.exception.TxnBizException;
import com.jrx.anytxn.common.exception.TxnException;
import com.jrx.anytxn.common.uitl.BeanMapping;
import com.jrx.anytxn.common.uitl.BeanUtils;
import com.jrx.anytxn.common.uitl.CollectorsUtils;
import com.jrx.anytxn.common.uitl.DateUtils;
import com.jrx.anytxn.common.uitl.SeqUtils;
import com.jrx.anytxn.customer.dto.customer.CustomerRes;
import com.jrx.anytxn.customer.dto.customer.CustomerSecondLevelRes;
import com.jrx.anytxn.customer.entity.CmCustomerBankcardInfo;
import com.jrx.anytxn.customer.mapper.ext.ExtCmCustomerBankcardInfoMapper;
import com.jrx.anytxn.number.exception.SegmentException;
import com.jrx.anytxn.number.service.ISegmentService;
import com.jrx.anytxn.param.constant.Constant;
import com.jrx.anytxn.param.dto.system.SystemParameterRes;
import com.jrx.anytxn.param.entity.PrOrganizationTable;
import com.jrx.anytxn.param.entity.PrProductTable;
import com.jrx.anytxn.param.enums.BizTypeEnum;
import com.jrx.anytxn.param.service.product.IProductTableService;
import com.jrx.anytxn.param.service.system.IOrganizationTableService;
import com.jrx.anytxn.param.service.system.ISequenceNumberService;
import com.jrx.anytxn.param.service.system.ISystemParameterService;
import com.jrx.anytxn.param.utils.BizTypeProducts;
import com.jrx.anytxn.ribbon.config.GrayVersionConfig;
import com.jrx.anytxn.transaction.bean.BillResult;
import com.jrx.anytxn.transaction.bean.CalPayOffFeeBean;
import com.jrx.anytxn.transaction.bean.CurrentStatementBean;
import com.jrx.anytxn.transaction.bean.DebtBean;
import com.jrx.anytxn.transaction.bean.DebtDetailBean;
import com.jrx.anytxn.transaction.bean.RepaymentParamBean;
import com.jrx.anytxn.transaction.constant.TransBizConstant;
import com.jrx.anytxn.transaction.dto.common.SendSmsReq;
import com.jrx.anytxn.transaction.dto.payment.AdvanceSettleLoanRes;
import com.jrx.anytxn.transaction.dto.payment.AdvanceSettleTrialRes;
import com.jrx.anytxn.transaction.dto.payment.BillPayReq;
import com.jrx.anytxn.transaction.dto.payment.BillPayTrailReq;
import com.jrx.anytxn.transaction.dto.payment.BillPayTrailRes;
import com.jrx.anytxn.transaction.dto.payment.BillPrepayNextReq;
import com.jrx.anytxn.transaction.dto.payment.DebtDetailRes;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyDebtDetailRes;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyLoanRes;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyPaymentReq;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyTrialReq;
import com.jrx.anytxn.transaction.dto.payment.DelinquencyTrialRes;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayLoanRes;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayReq;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayTrialReq;
import com.jrx.anytxn.transaction.dto.payment.OnDuePayTrialRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentStatusQueryReq;
import com.jrx.anytxn.transaction.dto.payment.PaymentStatusQueryRes;
import com.jrx.anytxn.transaction.dto.payment.PaymentTrialReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayTrialReq;
import com.jrx.anytxn.transaction.dto.payment.PrePayTrialRes;
import com.jrx.anytxn.transaction.dto.payment.RefundReq;
import com.jrx.anytxn.transaction.entity.AmAccountMainInfo;
import com.jrx.anytxn.transaction.entity.AmInterestAccrualInfo;
import com.jrx.anytxn.transaction.entity.AmLoanAccountInfo;
import com.jrx.anytxn.transaction.entity.AmLoanMainInfo;
import com.jrx.anytxn.transaction.entity.AmRepaymentPlanInfo;
import com.jrx.anytxn.transaction.entity.TlGlInterfaceInfo;
import com.jrx.anytxn.transaction.entity.TlLoanStagingMapping;
import com.jrx.anytxn.transaction.entity.TlPaymentAllocationLog;
import com.jrx.anytxn.transaction.entity.TlPaymentLog;
import com.jrx.anytxn.transaction.entity.TlPaymentMapping;
import com.jrx.anytxn.transaction.entity.TlRefundLog;
import com.jrx.anytxn.transaction.entity.TlSmsLog;
import com.jrx.anytxn.transaction.entity.TlTransactionInfo;
import com.jrx.anytxn.transaction.handler.ILoanHandler;
import com.jrx.anytxn.transaction.handler.IPaymentHandler;
import com.jrx.anytxn.transaction.mapper.base.TlPaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.base.TlRefundLogMapper;
import com.jrx.anytxn.transaction.mapper.base.TlTransactionInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmAccountMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmInterestAccrualInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanAccountInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmLoanMainInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtAmRepaymentPlanInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlGlInterfaceInfoMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlLoanStagingMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentAllocationLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentLogMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlPaymentMappingMapper;
import com.jrx.anytxn.transaction.mapper.ext.ExtTlSmsLogMapper;
import com.jrx.anytxn.transaction.service.IDealPaymentService;
import com.jrx.anytxn.transaction.service.IDebtCalculationService;
import com.jrx.anytxn.transaction.service.IFeeService;
import com.jrx.anytxn.transaction.service.IGlInterfaceService;
import com.jrx.anytxn.transaction.service.IPaymentLogService;
import com.jrx.anytxn.transaction.service.IPaymentService;
import com.jrx.anytxn.transaction.service.IRepaymentPlanService;
import com.jrx.anytxn.transaction.service.ISmsService;
import com.jrx.anytxn.transaction.service.ITlTransactionInfoService;
import com.jrx.anytxn.transaction.utils.PaymentUtils;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 还款service实现
 *
 * @author haopu.zheng
 * date  2019/7/26
 **/
@Service
public class PaymentServiceImpl implements IPaymentService {
    private static final Logger logger = LoggerFactory.getLogger(PaymentServiceImpl.class);

    @Resource
    private TlPaymentLogMapper tlPaymentLogMapper;
    @Resource
    private ExtTlPaymentLogMapper extTlPaymentLogMapper;
    @Resource
    private ExtTlPaymentMappingMapper extTlPaymentMappingMapper;
    @Autowired
    private IRepaymentPlanService repaymentPlanService;
    @Autowired
    private IPaymentLogService paymentLogService;
    @Autowired
    private IFeeService feeService;
    @Autowired
    private IProductTableService productTableService;
    @Autowired
    private IDebtCalculationService debtCalculationService;
    @Autowired
    private IOrganizationTableService organizationTableService;
    @Resource
    private ExtAmLoanAccountInfoMapper extAmLoanAccountInfoMapper;
    @Autowired
    private IDealPaymentService dealPaymentService;
    @Resource
    private ExtTlPaymentAllocationLogMapper extTlPaymentAllocationLogMapper;
    @Resource
    private ExtAmLoanMainInfoMapper extAmLoanMainInfoMapper;
    @Autowired
    private ILoanHandler loanHandlerImpl;
    @Resource
    private ExtAmRepaymentPlanInfoMapper extAmRepaymentPlanInfoMapper;
    @Autowired
    private ITlTransactionInfoService tlTransactionInfoService;
    @Resource
    private TlTransactionInfoMapper tlTransactionInfoMapper;
    @Autowired
    private IGlInterfaceService glInterfaceService;
    @Resource
    private ExtAmAccountMainInfoMapper extAmAccountMainInfoMapper;
    @Resource
    private ExtAmInterestAccrualInfoMapper extAmInterestAccrualInfoMapper;
    @Resource
    private ExtTlGlInterfaceInfoMapper extTlGlInterfaceInfoMapper;
    @Resource
    private ExtTlLoanStagingMappingMapper extTlLoanStagingMappingMapper;
    @Autowired
    private ISegmentService segmentService;
    @Autowired
    private ISystemParameterService systemParameterServiceImpl;
    @Resource
    private ExtCmCustomerBankcardInfoMapper extCmCustomerBankcardInfoMapper;
    @Autowired
    private ISequenceNumberService sequenceNumberService;
    @Autowired
    private GrayVersionConfig grayVersionConfig;
    @Resource
    private TlRefundLogMapper tlRefundLogMapper;
    @Autowired
    private IPaymentHandler paymentHandler;
    @Autowired
    private ISystemParameterService iSystemParameterService;
    @Autowired
    private ISmsService iSmsService;
    @Autowired
    private ExtTlSmsLogMapper extTlSmsLogMapper;


    @Override
    public DelinquencyTrialRes overdueTrail(List<AmLoanMainInfo> loanMainInfos, List<AmLoanAccountInfo> loanAccountInfos, DelinquencyTrialReq paymentTrialReq) throws TxnBizException {
        //逾期订单信息返回列表
        List<DelinquencyLoanRes> delinquencyLoanResList = new ArrayList<>(loanAccountInfos.size());
        //总还款额
        BigDecimal totalAmt = BigDecimal.ZERO;
        PrOrganizationTable organization =
                organizationTableService.findByOrId(loanAccountInfos.get(0).getOrganizationId(), loanAccountInfos.get(0).getTenantId());
        Date bussinessDate = organization.getNextProcessingDate();

        //遍历订单 计算欠款
        for (AmLoanAccountInfo loanAccountInfo : loanAccountInfos) {
            //计算试算欠款
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, true, null, null, false);
            List<DebtDetailRes> debtDetailResList = BeanMapping.copyList(debtBean.getDebtDetailBeans(), DebtDetailRes.class);
            totalAmt = totalAmt.add(debtBean.getTotalAmount());
            //设置单笔订单返回
            DelinquencyLoanRes delinquencyLoanRes = new DelinquencyLoanRes();
            delinquencyLoanRes.setLoanId(loanAccountInfo.getLoanId());
            delinquencyLoanRes.setPlanList(debtDetailResList);
            delinquencyLoanRes.setTotalAmount(debtBean.getTotalAmount());
            delinquencyLoanResList.add(delinquencyLoanRes);
        }

        //若上送了还款金额  需进行还款分配试算
        if (paymentTrialReq.getRepayAmount() != null) {
            PrProductTable productTable = productTableService.findProByProIdAndOrgId(loanAccountInfos.get(0).getProductId(), loanAccountInfos.get(0).getOrganizationId(), loanAccountInfos.get(0).getTenantId());
            //小于总逾期欠款  且  小于产品配置的最小还款金额
            if (paymentTrialReq.getRepayAmount().compareTo(totalAmt) < 0 &&
                    paymentTrialReq.getRepayAmount().compareTo(productTable.getMinRepayAmount()) < 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_REPAY_AMOUNT_ERROR.getFormatMsg(productTable.getMinRepayAmount().toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_REPAY_AMOUNT_ERROR.getCode(),
                        TxnRespCode.PAYMENT_REPAY_AMOUNT_ERROR.getFormatUserTips(productTable.getMinRepayAmount().toString()));
            }
            if (paymentTrialReq.getRepayAmount().compareTo(totalAmt) > 0) {
                //不允许还款
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getFormatMsg(paymentTrialReq.getRepayAmount().toString(),
                        totalAmt.toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getCode(),
                        TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getUserTips());
            }

            List<TlPaymentAllocationLog> paymentAllocationLogs = dealPaymentService.calculationOverPay(loanMainInfos, loanAccountInfos, bussinessDate, paymentTrialReq.getRepayAmount());

            Map<String, List<TlPaymentAllocationLog>> loanAllocationLogMap = paymentAllocationLogs.stream().collect(Collectors.groupingBy(TlPaymentAllocationLog::getLoanId));

            totalAmt = BigDecimal.ZERO;
            delinquencyLoanResList = new ArrayList<>(loanAccountInfos.size());

            for (AmLoanMainInfo amLoanMainInfo : loanMainInfos) {
                DelinquencyLoanRes delinquencyLoanRes = new DelinquencyLoanRes();
                delinquencyLoanRes.setLoanId(amLoanMainInfo.getLoanId());
                List<DebtDetailRes> planList = new ArrayList<>();

                List<TlPaymentAllocationLog> allocationLogs = loanAllocationLogMap.get(amLoanMainInfo.getLoanId());

                Map<Integer, Map<String, BigDecimal>> repayMap = allocationLogs.stream()
                        .collect(Collectors.groupingBy(TlPaymentAllocationLog::getBillingTenor,
                                Collectors.groupingBy(TlPaymentAllocationLog::getConnectAccountType,
                                        CollectorsUtils.summingBigDecimal(TlPaymentAllocationLog::getPostingAmount))));

                BigDecimal loanRepayAmt = BigDecimal.ZERO;
                for (Integer tennor : repayMap.keySet()) {
                    DebtDetailRes debtDetailRes = new DebtDetailRes();
                    debtDetailRes.setTenor(tennor);

                    Map<String, BigDecimal> tenorRepayMap = repayMap.get(tennor);

                    debtDetailRes.setPrincipalAmount(Optional.ofNullable(tenorRepayMap.get(TransBizConstant.ACCOUNT_TYPE_06)).orElse(BigDecimal.ZERO));
                    debtDetailRes.setInterestAmount(Optional.ofNullable(tenorRepayMap.get(TransBizConstant.ACCOUNT_TYPE_04)).orElse(BigDecimal.ZERO));
                    debtDetailRes.setPenaltyAmount(Optional.ofNullable(tenorRepayMap.get(TransBizConstant.ACCOUNT_TYPE_11)).orElse(BigDecimal.ZERO));
                    debtDetailRes.setCompountAmount(Optional.ofNullable(tenorRepayMap.get(TransBizConstant.ACCOUNT_TYPE_12)).orElse(BigDecimal.ZERO));
                    debtDetailRes.setFeeAmount(Optional.ofNullable(tenorRepayMap.get(TransBizConstant.ACCOUNT_TYPE_03)).orElse(BigDecimal.ZERO));

                    loanRepayAmt = loanRepayAmt.add(debtDetailRes.getPrincipalAmount())
                            .add(debtDetailRes.getInterestAmount())
                            .add(debtDetailRes.getPenaltyAmount())
                            .add(debtDetailRes.getCompountAmount())
                            .add(debtDetailRes.getFeeAmount());

                    planList.add(debtDetailRes);
                }
                planList = planList.stream().sorted(Comparator.comparing(DebtDetailRes::getTenor)).collect(Collectors.toList());
                delinquencyLoanRes.setPlanList(planList);
                delinquencyLoanRes.setTotalAmount(loanRepayAmt);
                delinquencyLoanResList.add(delinquencyLoanRes);
                totalAmt = totalAmt.add(loanRepayAmt);
            }
        }
        //逾期还款返回赋值
        DelinquencyTrialRes delinquencyTrialRes = BeanMapping.copy(paymentTrialReq, DelinquencyTrialRes.class);
        delinquencyTrialRes.setTotalAmount(totalAmt);
        delinquencyTrialRes.setLoanList(delinquencyLoanResList);
        return delinquencyTrialRes;
    }

    @Override
    public AdvanceSettleTrialRes preterminationTrail(List<AmLoanMainInfo> loanMainInfos, List<AmLoanAccountInfo> loanAccountInfos, PaymentTrialReq paymentTrialReq) throws TxnBizException {
        //提前结清订单信息列表
        List<AdvanceSettleLoanRes> advanceSettleLoanResList = new ArrayList<>(loanMainInfos.size());
        //总欠款
        BigDecimal totalAmt = BigDecimal.ZERO;
        PrOrganizationTable organization =
                organizationTableService.findByOrId(loanMainInfos.get(0).getOrganizationId(), loanMainInfos.get(0).getTenantId());
        Date bussinessDate = organization.getNextProcessingDate();

        //遍历订单 计算欠款
        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            //获取订单未抛、正常、逾期状态的还款计划
            List<AmRepaymentPlanInfo> repaymentPlanInfos = repaymentPlanService.getPlanList(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId(),
                    Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                            TransBizConstant.LOAN_PLAN_STATUS_2));

            AmLoanAccountInfo loanAccountInfo = loanAccountInfos.stream()
                    .filter(accountInfo -> accountInfo.getLoanId().equals(loanMainInfo.getLoanId()))
                    .findFirst()
                    .orElseThrow(() -> new TxnBizException(TxnRespCode.NO_DATA.getCode(),
                            TxnRespCode.NO_DATA.getUserTips()));
            //计算试算欠款
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, repaymentPlanInfos, bussinessDate, null, null, null, false);
            // 提前结清手续费抛帐获取生成的费用交易账户
            CalPayOffFeeBean bean = feeService.getPayOffFeeAcct(loanMainInfo, loanAccountInfo, repaymentPlanInfos);

            if (null != bean) {
                //设置提前节结清罚费
                BigDecimal payOffFeeAmt = bean.getPayOffFeeAmt();
                debtBean.setPreFeeAmount(payOffFeeAmt);
                //累加因提前结清而产生的分期手续费
                BigDecimal installmentFee = bean.getInstallmentFeeSubBean().getTotalFeeAmt();
                debtBean.setFeeAmount(debtBean.getFeeAmount().add(installmentFee));
                //累加因提前结清而产生的利息
                BigDecimal intAmt = bean.getIntrSubBean().getTotalIntrAmt();
                debtBean.setInterestAmount(debtBean.getInterestAmount().add(intAmt));
                //每期重新累加相应的分期手续费和利息
                Map<Integer, BigDecimal> installmentFeeMap = bean.getInstallmentFeeSubBean().getInstallmentFeeAmtMap();
                Map<Integer, BigDecimal> intrAmtMap = bean.getIntrSubBean().getIntrAmtMap();
                debtBean.getDebtDetailBeans().forEach(debtDetailBean -> {
                    BigDecimal intrPerTenorAmt = Optional.ofNullable(intrAmtMap.get(debtDetailBean.getTenor())).orElse(BigDecimal.ZERO);
                    debtDetailBean.setInterestAmount(debtDetailBean.getInterestAmount().add(intrPerTenorAmt));

                    BigDecimal installmentPerTenorFee = Optional.ofNullable(installmentFeeMap.get(debtDetailBean.getTenor())).orElse(BigDecimal.ZERO);
                    debtDetailBean.setFeeAmount(debtDetailBean.getFeeAmount().add(installmentPerTenorFee));
                });
                //重置总金额
                debtBean.setTotalAmount(debtBean.getTotalAmount().add(payOffFeeAmt).add(installmentFee).add(intAmt));
            }

            logger.debug("订单->{}欠款额->{}", loanMainInfo.getLoanId(), debtBean.getTotalAmount());

            totalAmt = totalAmt.add(debtBean.getTotalAmount());

            //设置单笔返回
            AdvanceSettleLoanRes advanceSettleLoanRes = BeanMapping.copy(debtBean, AdvanceSettleLoanRes.class);
            advanceSettleLoanRes.setLoanId(loanMainInfo.getLoanId());

            DebtDetailBean debtDetailBean = debtBean.getDebtDetailBeans().stream().filter(detail -> loanAccountInfo.getCastTenor().equals(detail.getTenor())).findFirst().get();
            if (bussinessDate.compareTo(loanAccountInfo.getNextPaymentDate()) == 0) {
                advanceSettleLoanRes.setCurrentPrinAmount(debtDetailBean.getPrincipalAmount());
                advanceSettleLoanRes.setCurrentIntrAmount(debtDetailBean.getInterestAmount());
            } else {
                advanceSettleLoanRes.setCurrentPrinAmount(BigDecimal.ZERO);
                advanceSettleLoanRes.setCurrentIntrAmount(BigDecimal.ZERO);
            }

            //计算试算欠款
            if (TransBizConstant.LOAN_STATUS_2.equals(loanAccountInfo.getAccountStatus())) {
                DebtBean overDebt = debtCalculationService.getBalance(loanAccountInfo, repaymentPlanInfos, bussinessDate, true, null, null, false);
                advanceSettleLoanRes.setOverduePrin(overDebt.getPrincipalAmount());
                advanceSettleLoanRes.setOverdueIntr(overDebt.getInterestAmount());
            } else {
                advanceSettleLoanRes.setOverduePrin(BigDecimal.ZERO);
                advanceSettleLoanRes.setOverdueIntr(BigDecimal.ZERO);
            }

            advanceSettleLoanResList.add(advanceSettleLoanRes);
        }

        //提前结清返回赋值
        AdvanceSettleTrialRes advanceSettleTrialRes = BeanMapping.copy(paymentTrialReq, AdvanceSettleTrialRes.class);
        advanceSettleTrialRes.setLoanList(advanceSettleLoanResList);
        advanceSettleTrialRes.setTotalAmount(totalAmt);
        return advanceSettleTrialRes;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String overdueRepayment(DelinquencyPaymentReq delinquencyPaymentReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnBizException {

        //获取订单信息列表
        List<AmLoanAccountInfo> loanAccountInfos = repaymentParamBean.getLoanAccountInfoList();

        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanAccountInfos.get(0).getProductId(),
                repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId(), delinquencyPaymentReq.getTenantId());

        //还款流水与订单映射信息
        List<TlPaymentMapping> paymentMappings = new ArrayList<>(loanAccountInfos.size());

        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_OVERDUE,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), delinquencyPaymentReq.getRepayAmount(),
                repaymentParamBean.getOrg().getNextProcessingDate(), product, delinquencyPaymentReq);
        paymentLog.setUpdateBy(delinquencyPaymentReq.getUserId());
        paymentLog.setPayId(delinquencyPaymentReq.getSeqNo());


        //获取还款额
        BigDecimal paymentAmt = delinquencyPaymentReq.getRepayAmount();
        //遍历订单
        BigDecimal debtAmount = BigDecimal.ZERO;//总欠款
        for (AmLoanAccountInfo loanAccountInfo : loanAccountInfos) {
            //欠款计算 获取欠款额
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, true, null, null, false);
            BigDecimal debtAmt = debtBean.getTotalAmount();
            if (debtAmt.compareTo(BigDecimal.ZERO) <= 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getFormatMsg(loanAccountInfo.getLoanId(), debtAmt.toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getCode(),
                        TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getUserTips());

            }
            debtAmount = debtAmount.add(debtBean.getTotalAmount());

            TlPaymentMapping paymentMapping = paymentLogService.buildTlPaymentMapping(loanAccountInfo.getCustomerId(),
                    paymentLog.getSeqNo(), loanAccountInfo.getLoanId(), delinquencyPaymentReq.getTenantId());
            paymentMappings.add(paymentMapping);

        }

        //判断还款是否大于欠款
        if (paymentAmt.compareTo(debtAmount) > 0) {
            //不允许还款
            String errMsg = TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getFormatMsg(paymentAmt.toString(),
                    debtAmount.toString());
            logger.error(errMsg);
            throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_AMOUNT_OVER_DEBT_NOT_ALLOW.getUserTips());
        }
        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        if (!paymentMappings.isEmpty()) {
            extTlPaymentMappingMapper.batchInsert(paymentMappings);
        }

        return paymentLog.getSeqNo();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String preterminationRepayment(PaymentReq paymentReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnBizException {

        //获取订单信息列表
        List<AmLoanMainInfo> loanMainInfos = repaymentParamBean.getLoanMainInfoList();
        List<AmLoanAccountInfo> loanAccountInfos = repaymentParamBean.getLoanAccountInfoList();


        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanMainInfos.get(0).getProductId(),
                repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId(), paymentReq.getTenantId());

        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_PRETERMINTION,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), BigDecimal.ZERO,
                repaymentParamBean.getOrg().getNextProcessingDate(), product, paymentReq);
        paymentLog.setUpdateBy(paymentReq.getUserId());
        paymentLog.setPayId(paymentReq.getSeqNo());

        //总欠款
        BigDecimal totalAmt = BigDecimal.ZERO;
        //还款流水与订单映射信息
        List<TlPaymentMapping> paymentMappings = new ArrayList<>(loanMainInfos.size());

        //遍历订单
        for (AmLoanMainInfo loanMainInfo : loanMainInfos) {
            //获取订单未抛、正常、逾期状态的还款计划
            List<AmRepaymentPlanInfo> repaymentPlanInfos = repaymentPlanService.getPlanList(loanMainInfo.getLoanId(),
                    loanMainInfo.getCustomerId(), loanMainInfo.getTenantId(),
                    Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1,
                            TransBizConstant.LOAN_PLAN_STATUS_2));

            //获取对应的订单账户
            AmLoanAccountInfo loanAccountInfo = loanAccountInfos.stream()
                    .filter(accountInfo -> accountInfo.getLoanId().equals(loanMainInfo.getLoanId()))
                    .findFirst()
                    .orElseThrow(() -> new TxnBizException(TxnRespCode.NO_DATA.getCode(),
                            TxnRespCode.NO_DATA.getUserTips()));

            //欠款计算
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, repaymentPlanInfos, bussinessDate, null, null, null, false);

            //获取因提前结清而产生的提前结清罚费，利息以及分期手续费
            CalPayOffFeeBean bean = feeService.getPayOffFeeAcct(loanMainInfo, loanAccountInfo, repaymentPlanInfos);

            BigDecimal debtAmt = debtBean.getTotalAmount();
            if (null != bean) {
                //提前节结清罚费
                BigDecimal payOffFeeAmt = bean.getPayOffFeeAmt();
                //因提前结清而产生的分期手续费
                BigDecimal installmentFee = bean.getInstallmentFeeSubBean().getTotalFeeAmt();
                //因提前结清而产生的利息
                BigDecimal intAmt = bean.getIntrSubBean().getTotalIntrAmt();
                debtBean.setInterestAmount(debtBean.getInterestAmount().add(intAmt));
                debtBean.setFeeAmount(debtBean.getFeeAmount().add(installmentFee));
                debtAmt = debtBean.getTotalAmount().add(payOffFeeAmt).add(installmentFee).add(intAmt);
            }

            if (debtAmt.compareTo(BigDecimal.ZERO) <= 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getFormatMsg(loanAccountInfo.getLoanId(), debtAmt.toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getCode(),
                        TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getUserTips());

            }
            //总欠款
            totalAmt = totalAmt.add(debtAmt);

            //构建还款流水与订单映射信息
            TlPaymentMapping paymentMapping =
                    paymentLogService.buildTlPaymentMapping(loanMainInfo.getCustomerId(),
                            paymentLog.getSeqNo(), loanMainInfo.getLoanId(), paymentReq.getTenantId());

            paymentMappings.add(paymentMapping);
        }

        paymentLog.setAmount(totalAmt);
        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        if (!paymentMappings.isEmpty()) {
            extTlPaymentMappingMapper.batchInsert(paymentMappings);
        }

        return paymentLog.getSeqNo();
    }


    @Override
    public PaymentStatusQueryRes getPaymentStatus(PaymentStatusQueryReq paymentStatusQueryReq, String customerId) throws TxnBizException {

        //获取还款流水
        TlPaymentLog tlPaymentLog =
                extTlPaymentLogMapper.selectPaymentStatusByPaymentSeqNo(paymentStatusQueryReq.getPaymentSeqNo(),
                        customerId, paymentStatusQueryReq.getTenantId());
        List<TlPaymentMapping> tlPaymentMappingList =
                extTlPaymentMappingMapper.selectByPaymentSeqAndCustomerId(paymentStatusQueryReq.getPaymentSeqNo(),
                        customerId, paymentStatusQueryReq.getTenantId());
        if (tlPaymentLog == null || CollectionUtils.isEmpty(tlPaymentMappingList)) {
            throw new TxnBizException(TxnRespCode.NO_DATA.getCode(),
                    TxnRespCode.NO_DATA.getUserTips());
        }
        PaymentStatusQueryRes paymentStatusQueryRes = BeanMapping.copy(paymentStatusQueryReq, PaymentStatusQueryRes.class);

        switch (tlPaymentLog.getSumStatus()) {
            case TransBizConstant.PAYMENT_LOG_SUM_STATUS_S:
                paymentStatusQueryRes.setPaymentStatus(TransBizConstant.PAYMENT_LOG_QUERY_STATUS_0);
                paymentStatusQueryRes.setTotalAmount(tlPaymentLog.getAmount());
                getPaymentAmt(paymentStatusQueryRes, tlPaymentLog);
                break;
            case TransBizConstant.PAYMENT_LOG_SUM_STATUS_F:
                paymentStatusQueryRes.setPaymentStatus(TransBizConstant.PAYMENT_LOG_QUERY_STATUS_2);
                paymentStatusQueryRes.setFailReason(tlPaymentLog.getReturnMsg());
                break;
            case TransBizConstant.PAYMENT_LOG_SUM_STATUS_T:
            case TransBizConstant.PAYMENT_LOG_SUM_STATUS_D:
            case TransBizConstant.PAYMENT_LOG_SUM_STATUS_O:
            default:
                paymentStatusQueryRes.setPaymentStatus(TransBizConstant.PAYMENT_LOG_QUERY_STATUS_1);
                break;
        }
        return paymentStatusQueryRes;
    }

    private void getPaymentAmt(PaymentStatusQueryRes paymentStatusQueryRes, TlPaymentLog tlPaymentLog) {

        TlPaymentAllocationLog tlPaymentAllocationLog = new TlPaymentAllocationLog();
        tlPaymentAllocationLog.setCustomerId(tlPaymentLog.getCustomerId());
        tlPaymentAllocationLog.setTenantId(tlPaymentLog.getTenantId());
        tlPaymentAllocationLog.setSeqNo(tlPaymentLog.getSeqNo());
        List<TlPaymentAllocationLog> tlPaymentAllocationLogList =
                extTlPaymentAllocationLogMapper.selectListByCondition(tlPaymentAllocationLog);

        BigDecimal principalAmt =
                tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog1 -> TransBizConstant.ACCOUNT_TYPE_06.equals(tlPaymentAllocationLog1.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal interestAmt =
                tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog1 -> TransBizConstant.ACCOUNT_TYPE_04.equals(tlPaymentAllocationLog1.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal feeAmt =
                tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog1 -> TransBizConstant.ACCOUNT_TYPE_03.equals(tlPaymentAllocationLog1.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal penaltylAmt =
                tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog1 -> TransBizConstant.ACCOUNT_TYPE_11.equals(tlPaymentAllocationLog1.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal compoundAmt =
                tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog1 -> TransBizConstant.ACCOUNT_TYPE_12.equals(tlPaymentAllocationLog1.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        paymentStatusQueryRes.setPrincipalAmount(principalAmt);
        paymentStatusQueryRes.setInterestAmount(interestAmt);
        paymentStatusQueryRes.setFeeAmount(feeAmt);
        paymentStatusQueryRes.setPenaltyAmount(penaltylAmt);
        paymentStatusQueryRes.setCompoundAmount(compoundAmt);
    }

    @Override
    public PrePayTrialRes prePayTrail(AmLoanMainInfo loanMainInfo, AmLoanAccountInfo loanAccountInfo, PrePayTrialReq req) throws TxnException {
        PrePayTrialRes res = new PrePayTrialRes();

        PrOrganizationTable organization =
                organizationTableService.findByOrId(loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        Date bussinessDate = organization.getNextProcessingDate();

        //订单逾期不允许提前还款
        if (TransBizConstant.LOAN_STATUS_2.equals(loanAccountInfo.getAccountStatus())) {
            logger.error("订单->{}订单逾期不允许还款", loanAccountInfo.getLoanId());
            throw new TxnBizException(TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getCode(),
                    TxnRespCode.PAYMENT_LOAN_OVER_DUE_NOT_ALLOW.getUserTips());
        }

        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(),
                loanMainInfo.getOrganizationId(), loanMainInfo.getTenantId());
        //最小剩余本金
        BigDecimal minRemainPrincipal = product.getMinRemainPrincipal();

        //获取提前还款罚费
        BigDecimal prePayfeeAmt = feeService.getPrePayFee(loanMainInfo, loanAccountInfo);
        //计算欠款
        DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, null, null, null, false);

        if (req.getPrincipalAmount() != null) {//本金还款

            //欠款本金
            BigDecimal prinBal = debtBean.getPrincipalAmount();
            //还款本金
            BigDecimal repayPrin = req.getPrincipalAmount();
            //还款后剩余本金
            BigDecimal remainPrincipal = prinBal.subtract(repayPrin);
            if (remainPrincipal.compareTo(BigDecimal.ZERO) < 0) {
                String errMsg = TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getFormatMsg(repayPrin.toString(), prinBal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getCode(),
                        TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getUserTips());
            }

            //校验是否满足产品配置
            if (remainPrincipal.compareTo(BigDecimal.ZERO) > 0 && minRemainPrincipal.compareTo(remainPrincipal) > 0) {
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatMsg(repayPrin.toString(), prinBal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getCode(),
                        TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatUserTips(repayPrin.toString(), prinBal));
            }
            res.setPrincipalAmount(repayPrin);
            res.setInterestAmount(debtBean.getInterestAmount());
            res.setPenaltyAmount(debtBean.getPenaltyAmount());
            res.setFeeAmount(debtBean.getFeeAmount());
            res.setPreFeeAmount(prePayfeeAmt);
            res.setCompountAmount(debtBean.getCompountAmount());
            res.setTotalAmount(debtBean.getTotalAmount().subtract(debtBean.getPrincipalAmount()).add(repayPrin).add(prePayfeeAmt));
        } else {
            //总欠款
            BigDecimal totalDebtAmt = debtBean.getTotalAmount().add(prePayfeeAmt);
            //若总欠款小于总还款额 则提示错误
            if (totalDebtAmt.compareTo(req.getPaymentAmount()) < 0) {
                logger.error(TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getFormatMsg(req.getPaymentAmount(), totalDebtAmt));
                throw new TxnBizException(TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getCode(),
                        TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getUserTips());
            }
            //根据还款顺序计算要实际还的金额
            List<TlPaymentAllocationLog> tlPaymentAllocationLogList =
                    dealPaymentService.calculationPrePay(loanAccountInfo, loanMainInfo, bussinessDate, req.getPaymentAmount());

            //还款总本金
            BigDecimal principalAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_06.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

            //欠款本金
            BigDecimal remainPrincipalAmt = debtBean.getPrincipalAmount();

            //剩余本金
            BigDecimal remainPrincipal = remainPrincipalAmt.subtract(principalAmt);


            //校验是否满足产品配置
            if (remainPrincipal.compareTo(BigDecimal.ZERO) > 0 && minRemainPrincipal.compareTo(remainPrincipal) > 0) {
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatMsg(remainPrincipal, minRemainPrincipal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getCode(), TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatUserTips(remainPrincipal, minRemainPrincipal));
            }

            //实还利息
            BigDecimal interestAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_04.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            //实还费用
            BigDecimal feetAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_03.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

            //实还罚息
            BigDecimal penaltytAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_11.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

            //实还复利
            BigDecimal compountAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_12.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            res.setPrincipalAmount(principalAmt);
            res.setInterestAmount(interestAmt);
            res.setPenaltyAmount(penaltytAmt);
            res.setFeeAmount(feetAmt);
            res.setPreFeeAmount(prePayfeeAmt);
            res.setCompountAmount(compountAmt);
            res.setTotalAmount(req.getPaymentAmount());
        }

        //组装返回实体
        res.setUserId(req.getUserId());
        res.setLoanId(req.getLoanId());

        return res;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String prePay(PrePayReq prePayReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnException {

        //获取订单信息列表
        AmLoanMainInfo loanMainInfo = repaymentParamBean.getLoanMainInfoList().get(0);
        AmLoanAccountInfo loanAccountInfo = repaymentParamBean.getLoanAccountInfoList().get(0);


        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanMainInfo.getProductId(),
                repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId(), prePayReq.getTenantId());

        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_PREPAY,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), BigDecimal.ZERO,
                repaymentParamBean.getOrg().getNextProcessingDate(), product, prePayReq);
        paymentLog.setUpdateBy(prePayReq.getUserId());
        paymentLog.setPayId(prePayReq.getSeqNo());

        //欠款计算
        DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, null, null, null, false);


        //最小剩余本金
        BigDecimal minRemainPrincipal = product.getMinRemainPrincipal();

        //获取提前还款费用
        BigDecimal prePayFeeAmt = feeService.getPrePayFee(loanMainInfo, loanAccountInfo);

        if (prePayReq.getPrincipalAmount() != null) {//本金还款


            //欠款本金
            BigDecimal prinBal = debtBean.getPrincipalAmount();
            //还款本金
            BigDecimal repayPrin = prePayReq.getPrincipalAmount();
            //还款后剩余本金
            BigDecimal remainPrincipal = prinBal.subtract(repayPrin);
            if (remainPrincipal.compareTo(BigDecimal.ZERO) < 0) {
                String errMsg = TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getFormatMsg(repayPrin.toString(), prinBal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getCode(), TxnRespCode.PAYMENT_PRIN_AMOUNT_WRONG.getUserTips());
            }

            //校验是否满足产品配置
            if (remainPrincipal.compareTo(BigDecimal.ZERO) > 0 && minRemainPrincipal.compareTo(remainPrincipal) > 0) {
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatMsg(repayPrin.toString(), prinBal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getCode(),
                        TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatUserTips(repayPrin.toString(), prinBal));
            }

            //总还款金额
            BigDecimal totalAmt = debtBean.getTotalAmount().subtract(prinBal).add(repayPrin).add(prePayFeeAmt);
            if (totalAmt.compareTo(BigDecimal.ZERO) <= 0) {
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_ZERO.getFormatMsg(totalAmt.toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_ZERO.getCode(),
                        TxnRespCode.PAYMENT_AMOUNT_ZERO.getUserTips());
            }
            //设置还款流水金额
            paymentLog.setAmount(totalAmt);
        } else {
            //总欠款
            BigDecimal totalDebtAmt = debtBean.getTotalAmount().add(prePayFeeAmt);
            //若总欠款小于总还款额 则提示错误
            if (totalDebtAmt.compareTo(prePayReq.getPaymentAmount()) < 0) {
                logger.error(TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getFormatMsg(prePayReq.getPaymentAmount(), totalDebtAmt));
                throw new TxnBizException(TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getCode(),
                        TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getFormatMsg(prePayReq.getPaymentAmount(), totalDebtAmt));
            }
            //根据还款顺序计算要实际还的金额
            List<TlPaymentAllocationLog> tlPaymentAllocationLogList =
                    dealPaymentService.calculationPrePay(loanAccountInfo, loanMainInfo, bussinessDate, prePayReq.getPaymentAmount());


            //还款总本金
            BigDecimal principalAmt =
                    tlPaymentAllocationLogList.stream().filter(tlPaymentAllocationLog -> TransBizConstant.ACCOUNT_TYPE_06.equals(tlPaymentAllocationLog.getConnectAccountType())).map(TlPaymentAllocationLog::getPostingAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

            //欠款本金
            BigDecimal remainPrincipalAmt = debtBean.getPrincipalAmount();

            //剩余本金
            BigDecimal remainPrincipal = remainPrincipalAmt.subtract(principalAmt);

            //校验是否满足产品配置
            if (remainPrincipal.compareTo(BigDecimal.ZERO) > 0 && minRemainPrincipal.compareTo(remainPrincipal) > 0) {
                String errMsg = TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatMsg(remainPrincipal, minRemainPrincipal);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getCode(),
                        TxnRespCode.PAYMENT_AMOUNT_LESS_MIN.getFormatUserTips(remainPrincipal, minRemainPrincipal));
            }

            //设置还款流水金额
            paymentLog.setAmount(prePayReq.getPaymentAmount());
        }
        //构建还款流水与订单映射信息
        TlPaymentMapping paymentMapping = paymentLogService.buildTlPaymentMapping(loanMainInfo.getCustomerId(),
                paymentLog.getSeqNo(), loanMainInfo.getLoanId(), prePayReq.getTenantId());

        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        extTlPaymentMappingMapper.insertSelective(paymentMapping);

        return paymentLog.getSeqNo();
    }

    @Override
    public OnDuePayTrialRes onDuePayTrial(List<AmLoanAccountInfo> loanAccountInfos, OnDuePayTrialReq onDuePayTrialReq) throws TxnBizException {
        //正常订单信息返回列表
        List<OnDuePayLoanRes> onDuePayLoanResList = new ArrayList<>(loanAccountInfos.size());

        //总还款额
        BigDecimal totalAmt = BigDecimal.ZERO;

        PrOrganizationTable organization =
                organizationTableService.findByOrId(loanAccountInfos.get(0).getOrganizationId(),
                        loanAccountInfos.get(0).getTenantId());
        Date bussinessDate = organization.getNextProcessingDate();

        //遍历订单 计算欠款
        for (AmLoanAccountInfo loanAccountInfo : loanAccountInfos) {

            //计算试算欠款
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, null, true, null, false);
            totalAmt = totalAmt.add(debtBean.getTotalAmount());
            //设置单笔订单返回
            OnDuePayLoanRes onDuePayLoanRes = new OnDuePayLoanRes();
            onDuePayLoanRes.setLoanId(loanAccountInfo.getLoanId());
            onDuePayLoanRes.setTotalAmount(debtBean.getTotalAmount());
            onDuePayLoanRes.setPrincipalAmount(debtBean.getPrincipalAmount());
            onDuePayLoanRes.setInterestAmount(debtBean.getInterestAmount());
            onDuePayLoanRes.setPenaltyAmount(debtBean.getPenaltyAmount());
            onDuePayLoanRes.setCompountAmount(debtBean.getCompountAmount());
            onDuePayLoanRes.setFeeAmount(debtBean.getFeeAmount());
            onDuePayLoanResList.add(onDuePayLoanRes);
        }

        //正常还款返回赋值
        OnDuePayTrialRes onDuePayTrialRes = BeanMapping.copy(onDuePayTrialReq, OnDuePayTrialRes.class);
        onDuePayTrialRes.setTotalAmount(totalAmt);
        onDuePayTrialRes.setLoanList(onDuePayLoanResList);
        return onDuePayTrialRes;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public String onDuePay(OnDuePayReq onDuePayReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnBizException {

        //获取订单信息列表
        List<AmLoanAccountInfo> loanAccountInfos = repaymentParamBean.getLoanAccountInfoList();


        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(loanAccountInfos.get(0).getProductId(),
                repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId(), onDuePayReq.getTenantId());

        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_PAY,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), BigDecimal.ZERO,
                repaymentParamBean.getOrg().getNextProcessingDate(), product, onDuePayReq);
        paymentLog.setUpdateBy(onDuePayReq.getUserId());
        paymentLog.setPayId(onDuePayReq.getSeqNo());


        //总欠款
        BigDecimal totalAmt = BigDecimal.ZERO;
        //还款流水与订单映射信息
        List<TlPaymentMapping> paymentMappings = new ArrayList<>(loanAccountInfos.size());

        //遍历订单
        for (AmLoanAccountInfo loanAccountInfo : loanAccountInfos) {
            //欠款计算
            DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, null, true, null, false);
            BigDecimal debtAmt = debtBean.getTotalAmount();
            if (debtAmt.compareTo(BigDecimal.ZERO) <= 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getFormatMsg(loanAccountInfo.getLoanId(), debtAmt.toString());
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getCode(), TxnRespCode.PAYMENT_DEBT_AMOUNT_ZERO.getUserTips());

            }

            //总欠款
            totalAmt = totalAmt.add(debtBean.getTotalAmount());

            //构建还款流水与订单映射信息
            TlPaymentMapping paymentMapping =
                    paymentLogService.buildTlPaymentMapping(loanAccountInfo.getCustomerId(),
                            paymentLog.getSeqNo(), loanAccountInfo.getLoanId(), onDuePayReq.getTenantId());

            paymentMappings.add(paymentMapping);
        }

        //设置还款流水金额
        paymentLog.setAmount(totalAmt);

        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        if (!paymentMappings.isEmpty()) {
            extTlPaymentMappingMapper.batchInsert(paymentMappings);
        }

        return paymentLog.getSeqNo();
    }

    @Override
    public List<DelinquencyDebtDetailRes> queryDelinquencyDebtDetail(String customerId, String tenantId,
                                                                     String channel) throws TxnException {
        List<AmLoanAccountInfo> loanAccountInfoList =
                extAmLoanAccountInfoMapper.selectByCustAndChannelAndStatus(customerId, channel, tenantId, Collections.singletonList(TransBizConstant.LOAN_STATUS_2));

        if (CollectionUtils.isNotEmpty(loanAccountInfoList)) {
            List<DelinquencyDebtDetailRes> delinquencyDebtDetailResList = new ArrayList<>(loanAccountInfoList.size());
            PrOrganizationTable organization =
                    organizationTableService.findByOrId(loanAccountInfoList.get(0).getOrganizationId(), tenantId);
            Date bussinessDate = organization.getNextProcessingDate();
            //遍历订单 计算欠款
            for (AmLoanAccountInfo loanAccountInfo : loanAccountInfoList) {
                AmLoanMainInfo loanMainInfo = extAmLoanMainInfoMapper.selectByLoanId(loanAccountInfo.getLoanId(), loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(), loanAccountInfo.getChannel());

                //计算试算欠款
                DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, true, null, null, false);
                DelinquencyDebtDetailRes delinquencyDebtDetailRes = new DelinquencyDebtDetailRes();
                delinquencyDebtDetailRes.setPrinciple(debtBean.getPrincipalAmount());//本金
                delinquencyDebtDetailRes.setInterest(debtBean.getInterestAmount());//利息
                delinquencyDebtDetailRes.setFee(debtBean.getFeeAmount());//费用
                delinquencyDebtDetailRes.setPenaltyInterest(debtBean.getPenaltyAmount());//罚息
                delinquencyDebtDetailRes.setCompoundInterest(debtBean.getCompountAmount());//复利
                delinquencyDebtDetailRes.setLoanId(loanAccountInfo.getLoanId());//订单id
                delinquencyDebtDetailRes.setBorrowDate(loanMainInfo.getTxnTime());//贷款日期
                delinquencyDebtDetailRes.setLoanAmount(loanMainInfo.getLoanAmount());
                delinquencyDebtDetailRes.setRemainPrinciple(loanAccountInfo.getRemainPrincipal());
                delinquencyDebtDetailRes.setDelqCount(debtBean.getDebtDetailBeans().size());
                delinquencyDebtDetailResList.add(delinquencyDebtDetailRes);

            }
            return delinquencyDebtDetailResList;
        }
        return Collections.emptyList();
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public BillResult billPay(BillPayReq billPayReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnBizException {
        BillResult billResult = new BillResult();
        List<String> loanList = new ArrayList<>();
        //获取订单信息列表
        List<AmLoanAccountInfo> loanAccountInfos = repaymentParamBean.getLoanAccountInfoList();
        String organizationId = repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId();
        String tenantId = billPayReq.getTenantId();
        String productId = loanAccountInfos.get(0).getProductId();
        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId,
                organizationId, tenantId);

        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_BILLPAY,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), billPayReq.getPaymentAmount(),
                bussinessDate, product, billPayReq);
        paymentLog.setUpdateBy(billPayReq.getUserId());
        paymentLog.setPayId(billPayReq.getSeqNo());


        //总欠款
        BigDecimal totalAmt = BigDecimal.ZERO;
        BigDecimal overDueTotalAmt = BigDecimal.ZERO;
        BigDecimal normalAmt = BigDecimal.ZERO;

        int overCount = 0;
        //还款流水与订单映射信息
        List<TlPaymentMapping> paymentMappings = new ArrayList<>();

        loanAccountInfos.sort(Comparator.comparing(AmLoanAccountInfo::getAccountStatus).reversed().thenComparing(AmLoanAccountInfo::getCreateTime));
        Collections.reverse(loanAccountInfos);
        int paymentDd = repaymentParamBean.getCustomer().getSecondLevel().getPaymentDd();
        paymentDd = paymentHandler.getPaymentDayByProductId(bussinessDate, paymentDd, productId);
        int statementDd = repaymentParamBean.getCustomer().getSecondLevel().getStatementDd();
        statementDd = paymentHandler.getStatementDayByProductId(bussinessDate, statementDd, productId);
        CurrentStatementBean bean = loanHandlerImpl.getCurrentStatement(productId,statementDd, paymentDd, bussinessDate);
        Date paymentDate = bean.getPaymentDate();

        Date finalThisMonthPaymentDate = paymentDate;
        //遍历订单
        for (int i = loanAccountInfos.size() - 1; i >= 0; i--) {
            AmLoanAccountInfo loanAccountInfo = loanAccountInfos.get(i);
            //欠款计算
            BigDecimal debtAmt = BigDecimal.ZERO;
            if (TransBizConstant.LOAN_STATUS_2.equals(loanAccountInfo.getAccountStatus())) {
                //先计算逾期部分金额
                DebtBean overDebtBean = debtCalculationService.getBalance(loanAccountInfo, bussinessDate, true, null, null,
                        null);
                overDueTotalAmt = overDueTotalAmt.add(overDebtBean.getTotalAmount());

                overCount++;
                loanList.add(loanAccountInfo.getLoanId());
                debtAmt = overDebtBean.getTotalAmount();

                //再计算当期部分金额
                List<AmRepaymentPlanInfo> repaymentPlanInfos = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanAccountInfo.getLoanId(), Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_1),
                        loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId());
                if (CollectionUtils.isNotEmpty(repaymentPlanInfos)) {
                    repaymentPlanInfos = repaymentPlanInfos.stream().filter(plan -> finalThisMonthPaymentDate.equals(plan.getPaymentDueDate())).collect(Collectors.toList());
                    DebtBean normalDebtBean = debtCalculationService.getBalance(loanAccountInfo, repaymentPlanInfos, bussinessDate, null, null, null, false);
                    normalAmt = normalAmt.add(normalDebtBean.getTotalAmount());
                    debtAmt = debtAmt.add(normalDebtBean.getTotalAmount());
                }

            } else {
                //如果还款金额小于逾期欠款
                if (overDueTotalAmt.compareTo(billPayReq.getPaymentAmount()) > 0) {
                    logger.info("逾期欠款:{},还款金额:{}", overDueTotalAmt, billPayReq.getPaymentAmount());
                    continue;
                }

                //List<AmRepaymentPlanInfo> repaymentPlanInfos = repaymentPlanService.getPlanList(loanAccountInfo.getLoanId(),
                //        loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId(),
                //        Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_1));
                //查询订单还款计划
                List<AmRepaymentPlanInfo> repaymentPlanInfos = extAmRepaymentPlanInfoMapper.selectByLoanIdAndStatus(loanAccountInfo.getLoanId()
                        , Collections.singletonList(TransBizConstant.LOAN_PLAN_STATUS_1), loanAccountInfo.getCustomerId(), loanAccountInfo.getTenantId());
                if (CollectionUtils.isEmpty(repaymentPlanInfos)) {
                    continue;
                }

                repaymentPlanInfos = repaymentPlanInfos.stream().filter(plan -> finalThisMonthPaymentDate.equals(plan.getPaymentDueDate())).collect(Collectors.toList());
                DebtBean debtBean = debtCalculationService.getBalance(loanAccountInfo, repaymentPlanInfos, bussinessDate, null, null, null, false);

                //总欠款
                totalAmt = totalAmt.add(debtBean.getTotalAmount());
                //正常部分欠款
                normalAmt = normalAmt.add(debtBean.getTotalAmount());

                loanList.add(loanAccountInfo.getLoanId());
                debtAmt = debtBean.getTotalAmount();
            }

            if (debtAmt.compareTo(BigDecimal.ZERO) <= 0) {
                loanAccountInfos.remove(i);
                continue;
            }

            //构建还款流水与订单映射信息
            TlPaymentMapping paymentMapping =
                    paymentLogService.buildTlPaymentMapping(loanAccountInfo.getCustomerId(),
                            paymentLog.getSeqNo(), loanAccountInfo.getLoanId(), tenantId);
            paymentMappings.add(paymentMapping);
        }

        String paymentType = "";
        if (overCount > 0) {
            //如果还款金额>当期账单金额
            if (billPayReq.getPaymentAmount().compareTo(overDueTotalAmt.add(normalAmt)) > 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_BILLOVER_AMOUNT_WRONG.getFormatMsg(billPayReq.getPaymentAmount(), overDueTotalAmt.add(normalAmt));
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_BILLOVER_AMOUNT_WRONG.getCode(),
                        TxnRespCode.PAYMENT_BILLOVER_AMOUNT_WRONG.getUserTips());
            }

            if (billPayReq.getPaymentAmount().compareTo(overDueTotalAmt) > 0) {
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLOVERDUE_PREPAYCURR;
            } else {
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLOVERDUEPAY;
            }
        } else {
            if (totalAmt.compareTo(BigDecimal.ZERO) == 0) {
                throw new TxnBizException(TxnRespCode.PAYMENT_BILL_NOT_READY.getCode(),
                        TxnRespCode.PAYMENT_BILL_NOT_READY.getUserTips());
            }

            //出账单提前还当期,金额必须小于等于当期欠款
            if (billPayReq.getPaymentAmount().compareTo(totalAmt) > 0) {
                String errMsg =
                        TxnRespCode.PAYMENT_BILL_AMOUNT_WRONG.getFormatMsg(billPayReq.getPaymentAmount(), totalAmt);
                logger.error(errMsg);
                throw new TxnBizException(TxnRespCode.PAYMENT_BILL_AMOUNT_WRONG.getCode(),
                        TxnRespCode.PAYMENT_BILL_AMOUNT_WRONG.getUserTips());
            }

            //账单正常还款
            if (bussinessDate.compareTo(paymentDate) == 0) {
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLPAY;
            } else {
                //账单提前还当期
                paymentType = TransBizConstant.REPAYMENT_TYPE_BILLPREPAYCURR;
            }
        }


        //设置还款类型
        paymentLog.setPaymentType(paymentType);
        paymentLog.setDescription(product.getChineseName() + PaymentUtils.getPaymentTypeName(paymentType));//交易说明	　


        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        if (!paymentMappings.isEmpty()) {
            extTlPaymentMappingMapper.batchInsert(paymentMappings);
        }

        billResult.setLoanList(loanList);
        billResult.setSeqNo(paymentLog.getSeqNo());
        return billResult;
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public BillResult billPrepayNext(BillPrepayNextReq billPrepayNextReq, RepaymentParamBean repaymentParamBean, Date bussinessDate) throws TxnException {
        CustomerSecondLevelRes second = repaymentParamBean.getCustomer().getSecondLevel();
        String customerId = second.getCustomerId();
        String tenantId = billPrepayNextReq.getTenantId();
        //还款金额
        BigDecimal payAmt = billPrepayNextReq.getPaymentAmount();

        List<AmLoanAccountInfo> loanAccountInfos = repaymentParamBean.getLoanAccountInfoList();
        if (loanAccountInfos.stream().anyMatch(loan -> TransBizConstant.LOAN_STATUS_2.equals(loan.getAccountStatus()))) {
            throw new TxnBizException(TxnRespCode.PAYMENT_BILL_OVER.getCode(),
                    TxnRespCode.PAYMENT_BILL_OVER.getUserTips());
        }
        String productId = loanAccountInfos.get(0).getProductId();
        Integer paymentDd = second.getPaymentDd();
        paymentDd = paymentHandler.getPaymentDayByProductId(bussinessDate, paymentDd, productId);
        Integer statementDd = second.getStatementDd();
        statementDd = paymentHandler.getStatementDayByProductId(bussinessDate, statementDd, productId);
        CurrentStatementBean bean = loanHandlerImpl.getCurrentStatement(productId, statementDd, paymentDd, bussinessDate);
        //当期还款日
        Date onDuePaymentDate = bean.getPaymentDate();
        //下期还款日
        Date nextPaymentDate = DateUtils.addMonth(onDuePaymentDate, 1);

        //当期（包含逾期）欠款
        BillPayTrailReq req = new BillPayTrailReq();
        BeanMapping.copy(billPrepayNextReq, req);
        req.setPaymentMonth(DateUtils.formatDate(onDuePaymentDate, DateUtils.YEAR_MONTH_PATTERN));
        BillPayTrailRes onDueRes = paymentHandler.billPayTrail(req);
        BigDecimal onDueAmount = onDueRes.getAmount();

        //下期欠款
        req.setPaymentMonth(DateUtils.formatDate(nextPaymentDate, DateUtils.YEAR_MONTH_PATTERN));
        BillPayTrailRes nextRes = paymentHandler.billPayTrail(req);
        BigDecimal nextAmount = nextRes.getAmount();


        //还款金额必须小于等于于下期账单欠款
        if (payAmt.compareTo(nextAmount) > 0) {
            String errMsg =
                    TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getFormatMsg(payAmt, onDueAmount.add(nextAmount));
            logger.error(errMsg);
            throw new TxnBizException(TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getCode(),
                    TxnRespCode.PAYMENT_TOTAL_AMOUNT_WRONG.getUserTips());
        }

        //金额小于等于当期欠款
        if (payAmt.compareTo(onDueAmount) <= 0) {
            //提前还当期
            BillPayReq billPayReq = new BillPayReq();
            BeanMapping.copy(billPrepayNextReq, billPayReq);
            return this.billPay(billPayReq, repaymentParamBean, bussinessDate);
        }

        //金额大于当期欠款,结清当期,提前还下期
        //获取订单信息列表
        List<String> loanIdList = loanAccountInfos.stream().map(AmLoanAccountInfo::getLoanId).collect(Collectors.toList());

        List<String> paymentLoanIdList = new ArrayList<>();
        if (onDueAmount.compareTo(BigDecimal.ZERO) > 0) {

            //获取所有当期还款日未结清的还款计划
            List<AmRepaymentPlanInfo> onDuePlanList =
                    extAmRepaymentPlanInfoMapper.selectByLoanIdList(customerId, tenantId, loanIdList, onDuePaymentDate);
            paymentLoanIdList.addAll(onDuePlanList.stream().filter(plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_1, TransBizConstant.LOAN_PLAN_STATUS_2).contains(plan.getPlanStatus()))
                    .map(AmRepaymentPlanInfo::getLoanId).collect(Collectors.toList()));

        }

        //还完当期后剩余可还金额
        BigDecimal remainAmt = payAmt.subtract(onDueAmount);
        //获取所有下期还款日未结清的还款计划
        List<AmRepaymentPlanInfo> nextPlanList =
                extAmRepaymentPlanInfoMapper.selectByLoanIdList(customerId, tenantId, loanIdList, nextPaymentDate);
        //还款计划按创建时间排序
        nextPlanList = nextPlanList.stream().filter(plan -> Arrays.asList(TransBizConstant.LOAN_PLAN_STATUS_0, TransBizConstant.LOAN_PLAN_STATUS_1).contains(plan.getPlanStatus()))
                .sorted(Comparator.comparing(AmRepaymentPlanInfo::getCreateTime)).collect(Collectors.toList());
        for (AmRepaymentPlanInfo planInfo : nextPlanList) {
            String loanId = planInfo.getLoanId();
            if (paymentLoanIdList.contains(loanId)) {
                continue;
            }
            //计算欠款
            DebtBean debtBean = debtCalculationService.getBalance(planInfo);
            BigDecimal debtAmt = debtBean.getTotalAmount();
            if (debtAmt.compareTo(BigDecimal.ZERO) > 0) {
                paymentLoanIdList.add(loanId);
            }
            remainAmt = remainAmt.subtract(debtAmt);
            if (remainAmt.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }


        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId,
                repaymentParamBean.getCustomer().getSecondLevel().getOrganizationId(), billPrepayNextReq.getTenantId());
        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_BILLPREPAY,
                repaymentParamBean.getBank(), repaymentParamBean.getCustomer(), payAmt,
                bussinessDate, product, billPrepayNextReq);
        paymentLog.setUpdateBy(billPrepayNextReq.getUserId());
        paymentLog.setPayId(billPrepayNextReq.getSeqNo());

        //还款流水与订单映射信息
        List<TlPaymentMapping> paymentMappings = new ArrayList<>();
        for (String loanId : paymentLoanIdList) {
            AmLoanAccountInfo loanAccountInfo = loanAccountInfos.stream().filter(loan -> loan.getLoanId().equals(loanId)).findFirst().get();
            //构建还款流水与订单映射信息
            TlPaymentMapping paymentMapping =
                    paymentLogService.buildTlPaymentMapping(loanAccountInfo.getCustomerId(),
                            paymentLog.getSeqNo(), loanAccountInfo.getLoanId(), tenantId);
            paymentMappings.add(paymentMapping);
        }

        //流水及流水订单映射入库
        tlPaymentLogMapper.insertSelective(paymentLog);
        if (CollectionUtils.isNotEmpty(paymentMappings)) {
            extTlPaymentMappingMapper.batchInsert(paymentMappings);
        }
        BillResult billResult = new BillResult();
        billResult.setSeqNo(paymentLog.getSeqNo());
        billResult.setLoanList(paymentLoanIdList);
        return billResult;
    }

    /**
     * 退款
     *
     * @param loanMainInfo
     * @param refundReq
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void refund(RefundReq refundReq, RepaymentParamBean repaymentParamBean, AmLoanMainInfo loanMainInfo, List<String> mistakeLoanIds) throws Exception {
        logger.info("退款响应参数:{}", JSON.toJSONString(refundReq));
        BigDecimal refundAmt = refundReq.getRefundAmount();
        String seqNo = refundReq.getSeqNo();
        String tenantId = refundReq.getTenantId();
        String customerId = loanMainInfo.getCustomerId();
        String channel = loanMainInfo.getChannel();
        String orgId = loanMainInfo.getOrganizationId();
        String productId = loanMainInfo.getProductId();
        CustomerRes customer = repaymentParamBean.getCustomer();
        CmCustomerBankcardInfo bank = repaymentParamBean.getBank();
        //产品对应的业务类型 兴安花 、兴安付 、信用就医
        BizTypeEnum bizType = BizTypeProducts.getBizType(loanMainInfo.getProductId());
        List<String> bizTypeProductCodes = BizTypeProducts.getByBizType(bizType);
        PrOrganizationTable org = organizationTableService.findByOrId(TransBizConstant.DEFAULT_ORG_ID, tenantId);
        Date businessDate = org.getNextProcessingDate();

        //生成退款交易流水
        TlTransactionInfo tlTransactionInfo = tlTransactionInfoService.buildRefundTransInfo(loanMainInfo, refundAmt, businessDate, seqNo);
        tlTransactionInfoMapper.insertSelective(tlTransactionInfo);

        AmLoanAccountInfo loanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(loanMainInfo.getLoanId(), customerId, tenantId);
        //获取产品相关信息
        PrProductTable product = productTableService.findProByProIdAndOrgId(productId, orgId, tenantId);
        //构建还款流水信息
        TlPaymentLog paymentLog = paymentLogService.buildTlPaymentLog(TransBizConstant.REPAYMENT_TYPE_REFUND, bank, customer, refundAmt,  businessDate, product, refundReq);
        paymentLog.setUpdateBy(refundReq.getUserId());
        paymentLog.setPayId(seqNo);
        paymentLog.setRefNo(seqNo);
        //方便后续统计退款来源
        paymentLog.setSubChannel(loanMainInfo.getSubChannel());
        paymentLog.setThirdChannel(loanMainInfo.getThirdChannel());
        //退款不做扣款动作
        paymentLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_T);
        paymentLog.setCompleteTime(DateUtils.getBusinessTime(new Date(), businessDate));
        paymentLog.setMerchantId(refundReq.getMerchantId());
        paymentLog.setMerchantName(refundReq.getMerchantName());

        //还款流水与订单映射信息

        //剩余未还订单
        List<AmLoanAccountInfo> loanAccountInfos =
                extAmLoanAccountInfoMapper.selectByCustomerIdAndLoanTypeAndNotStatusWithProducts(tenantId,
                        customerId, channel, Arrays.asList(TransBizConstant.ACCOUNT_STATUS_0,
                                TransBizConstant.ACCOUNT_STATUS_6, TransBizConstant.ACCOUNT_STATUS_7,
                                TransBizConstant.ACCOUNT_STATUS_8, TransBizConstant.ACCOUNT_STATUS_9),
                        Arrays.asList(TransBizConstant.LOAN_TYPE_2, TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_4, TransBizConstant.LOAN_TYPE_5, TransBizConstant.LOAN_TYPE_7),bizTypeProductCodes);

        TlLoanStagingMapping stagingMapping = extTlLoanStagingMappingMapper.selectByCustIdAndOrigLoanId(customerId, tenantId, loanAccountInfo.getLoanId());
        if (Objects.nonNull(stagingMapping)) {
            //获取分期订单
            loanAccountInfo = extAmLoanAccountInfoMapper.selectByLoanId(stagingMapping.getStagingLoanId(), customerId, tenantId);
            loanMainInfo = extAmLoanMainInfoMapper.selectByLoanIdAndCusId(stagingMapping.getStagingLoanId(), customerId, tenantId);
        }

        //如果订单已结清，还到哪笔算哪笔，如果还完了,还有剩余退款金额，做一笔转账
        if (Arrays.asList(TransBizConstant.LOAN_STATUS_7, TransBizConstant.LOAN_STATUS_8).contains(loanAccountInfo.getAccountStatus())) {
            this.repayAllLoan(loanMainInfo, loanAccountInfos, customer, paymentLog, businessDate, refundAmt, mistakeLoanIds);
        } else {
            //未结清
            //如果大于剩余本金，先冲销全部利，罚，复，再对客户剩余订单依次还款,如果还有剩余再转账
            if (refundAmt.compareTo(loanAccountInfo.getRemainPrincipal()) < 0) {
                //冲销利，罚，复
                this.revertAcct(loanAccountInfo, refundAmt, businessDate, false);
                this.revertAccuAcct(loanAccountInfo, refundAmt, businessDate, false);

                loanAccountInfos.clear();
                loanAccountInfos.add(loanAccountInfo);

                //还款
                this.repayAllLoan(loanMainInfo, loanAccountInfos, customer, paymentLog, businessDate, refundAmt, mistakeLoanIds);
            } else {
                //冲销利，罚，复
                this.revertAcct(loanAccountInfo, refundAmt, businessDate, true);
                this.revertAccuAcct(loanAccountInfo, refundAmt, businessDate, true);
                //还款
                this.repayAllLoan(loanMainInfo, loanAccountInfos, customer, paymentLog, businessDate, refundAmt, mistakeLoanIds);
            }
        }

    }

    /**
     * 还款并转账
     *
     * @param loanMainInfo
     * @param loanAccountInfos
     * @param paymentLog
     * @param businessDate
     * @param refundAmt
     * @throws TxnBizException
     */
    @Transactional(rollbackFor = Exception.class)
    public void repayAllLoan(AmLoanMainInfo loanMainInfo, List<AmLoanAccountInfo> loanAccountInfos, CustomerRes customer, TlPaymentLog paymentLog,
                              Date businessDate, BigDecimal refundAmt, List<String> mistakeLoanIds) throws TxnException, SegmentException {
        BigDecimal payAmt = BigDecimal.ZERO;
        String tenantId = loanMainInfo.getTenantId();
        String customerId = loanMainInfo.getCustomerId();

        List<AmLoanMainInfo> loanMainInfoList = extAmLoanMainInfoMapper.selectByCusIdtAndTenantId(customerId, tenantId);


        //过滤掉还款处理中的订单或者有差错的订单
        loanAccountInfos.removeIf(loan -> mistakeLoanIds.contains(loan.getLoanId()));
        loanAccountInfos.removeIf(loan -> paymentHandler.repaymentLogStatusFlag(loan.getLoanId(), customerId, tenantId)
                || paymentHandler.repaymentWaiveLogStatusFlag(loan.getLoanId(), customerId, tenantId));
        //过滤掉分期未成功的订单
        loanAccountInfos.removeIf(
                loan -> {
                    AmLoanMainInfo info = loanMainInfoList.stream().filter(loanMain -> loan.getLoanId().equals(loanMain.getLoanId())).findFirst().get();
                    if (!Arrays.asList(TransBizConstant.LOAN_TYPE_3, TransBizConstant.LOAN_TYPE_7).contains(info.getLoanType())) {
                        return false;
                    }
                    List<TlLoanStagingMapping> stagingMappingList = extTlLoanStagingMappingMapper
                            .selectByCustIdAndStagingLoanId(info.getCustomerId(), info.getTenantId(), info.getLoanId());
                    if (CollectionUtils.isEmpty(stagingMappingList)) {
                        return false;
                    }
                    return stagingMappingList.stream().anyMatch(mapping -> !TransBizConstant.SUM_STATUS_S.equals(mapping.getSumStatus()));
                }
        );

        //没有需要还款的订单直接溢缴款
        if (CollectionUtils.isEmpty(loanAccountInfos)) {
            this.generateRefundLog(loanMainInfo, customer, "", businessDate, refundAmt, paymentLog.getPayId(), paymentLog.getThirdChannel());
            return;
        }


        //按借款时间排序
        loanAccountInfos.sort((l1, l2) -> {
            AmLoanMainInfo info1 = loanMainInfoList.stream().filter(info -> l1.getLoanId().equals(info.getLoanId())).findFirst().get();
            AmLoanMainInfo info2 = loanMainInfoList.stream().filter(info -> l2.getLoanId().equals(info.getLoanId())).findFirst().get();
            return info1.getPostingDate().compareTo(info2.getPostingDate());
        });

        //如果原订单未结清，先结清原订单
        loanAccountInfos.stream().filter(info -> loanMainInfo.getLoanId().equals(info.getLoanId())).findFirst().ifPresent(
                info -> Collections.swap(loanAccountInfos, 0, loanAccountInfos.indexOf(info)));

        List<TlPaymentMapping> paymentMappings = new ArrayList<>();
        //轮询未还完订单
        for (AmLoanAccountInfo loan : loanAccountInfos) {

            DebtBean bean = debtCalculationService.getBalance(loan, businessDate, null, null, null, null);
            BigDecimal debtAmt = bean.getTotalAmount();
            if (refundAmt.compareTo(debtAmt) > 0) {
                payAmt = payAmt.add(debtAmt);
            } else {
                payAmt = payAmt.add(refundAmt);
            }
            //构建还款流水与订单映射信息
            TlPaymentMapping paymentMapping =
                    paymentLogService.buildTlPaymentMapping(loan.getCustomerId(),
                            paymentLog.getSeqNo(), loan.getLoanId(), tenantId);
            paymentMappings.add(paymentMapping);
            refundAmt = refundAmt.subtract(debtAmt);
            if (refundAmt.compareTo(BigDecimal.ZERO) <= 0) {
                break;
            }
        }

        paymentLog.setAmount(payAmt);
        logger.info("退款流水表参数:{}", JSON.toJSONString(paymentLog));
        tlPaymentLogMapper.insertSelective(paymentLog);
        extTlPaymentMappingMapper.batchInsert(paymentMappings);


        if (refundAmt.compareTo(BigDecimal.ZERO) > 0) {
            this.generateRefundLog(loanMainInfo, customer, paymentLog.getSeqNo(), businessDate, refundAmt, paymentLog.getPayId(), paymentLog.getThirdChannel());
        }
    }

    /**
     * 生成溢缴款转账流水
     *
     * @param loanMainInfo
     * @param customer
     * @param seqNo
     * @param businessDate
     * @param refundAmt
     * @param payId
     * @param thirdChannel
     * @throws SegmentException
     * @throws TxnException
     */
    private void generateRefundLog(AmLoanMainInfo loanMainInfo, CustomerRes customer, String seqNo,
                                   Date businessDate, BigDecimal refundAmt, String payId, String thirdChannel) throws SegmentException, TxnException {
        TlRefundLog refundLog = new TlRefundLog();
        refundLog.setId(segmentService.getId(TransBizConstant.TL_REFUND_LOG));
        refundLog.setTenantId(loanMainInfo.getTenantId());
        refundLog.setOrganizationId(loanMainInfo.getOrganizationId());
        refundLog.setChannel(loanMainInfo.getChannel());
        refundLog.setProductId(loanMainInfo.getProductId());
        refundLog.setPayId(payId);
        refundLog.setSubChannel(loanMainInfo.getThirdChannel());
        //refundLog.setThirdChannel(loanMainInfo.getThirdChannel());
        refundLog.setThirdChannel(thirdChannel);
        refundLog.setCustomerId(loanMainInfo.getCustomerId());
        refundLog.setBusinessDate(businessDate);
        refundLog.setBusinessNo(TransBizConstant.REFUND_LOG_SEQ_PREFIX + SeqUtils.getSNByLength(TransBizConstant.PAYMENT_LOG_SEQ_RADOM_LENGTH));//流水号	唯一索引 RL+yyyyMMddHHmmssSSS +13位随机);
        refundLog.setEcifNo(customer.getFirstLevel().getEcifId());

        refundLog.setAmount(refundAmt);
        refundLog.setCommissionDate(new Date());
        refundLog.setCommissiontime(new Date());
        SystemParameterRes systemAcctNo = systemParameterServiceImpl.findByParamCode(TransBizConstant.REFUND_ACCT_NO + loanMainInfo.getThirdChannel(), loanMainInfo.getTenantId());
        refundLog.setMediumAcctNo(systemAcctNo.getParamValue());
        refundLog.setAcctNo(systemAcctNo.getParamValue());
        SystemParameterRes systemAcctName = systemParameterServiceImpl.findByParamCode(TransBizConstant.REFUND_ACCT_NAME + loanMainInfo.getThirdChannel(), loanMainInfo.getTenantId());
        refundLog.setAcctName(systemAcctName.getParamValue());
        SystemParameterRes systemAcctBranch = systemParameterServiceImpl.findByParamCode(TransBizConstant.REFUND_ACCT_BRANCH + loanMainInfo.getThirdChannel(), loanMainInfo.getTenantId());
        refundLog.setAcctBranch(systemAcctBranch.getParamValue());
        refundLog.setAcctType("");
        /**
         * 产生溢缴款的时候
         * 1.有一类卡默认退款至一类卡
         * 2.没有一类卡时，有微信还款记录则退回到微信
         * 3.都没有时返回绑定一类卡提醒
         */
        CustomerSecondLevelRes secondLevel = customer.getSecondLevel();
        String customerId = secondLevel.getCustomerId();
        String tenantId = secondLevel.getTenantId();
        //8 查询客户默认收款卡
        List<CmCustomerBankcardInfo> cardList = extCmCustomerBankcardInfoMapper.selectByDefaultGetFlag(loanMainInfo.getCustomerId(), loanMainInfo.getChannel(),
                loanMainInfo.getTenantId(), TransBizConstant.Y_FLAG);
        //1.没有一类卡
        if (CollectionUtils.isEmpty(cardList)) {
            List<TlPaymentLog> paymentLogList = extTlPaymentLogMapper.selectByCustTenantIdProductIdsAndNotSumStatusDesc(customerId, tenantId, TransBizConstant.PAYMENT_LOG_SUM_STATUS_F,
                    Collections.singletonList(loanMainInfo.getProductId()));
            // 有还款记录则使用微信退款
            if (CollectionUtils.isNotEmpty(paymentLogList)) {
                try {
                    SystemParameterRes transitionalAcctRes = iSystemParameterService.findByParamCode(TransBizConstant.INNER_TRANSITIONAL_BANK_ACCT , tenantId);
                    refundLog.setToAcctNo(transitionalAcctRes.getParamValue());
                    refundLog.setToAcctName(transitionalAcctRes.getParamDescription());
                    refundLog.setToAcctBranch("102141013021");
                    refundLog.setToAcctBranchName("厦门国际银行");
                    refundLog.setInstdPty("102141013021");
                    // 微信退款标志的渠道-还款流水表+交易流水表等
                    refundLog.setThirdChannel(Constant.THIRD_CHANNEL);
                } catch (TxnException e) {
                    logger.error("用户{}获取还款的内部户出错了{}", customerId, e);
                    throw new TxnBizException(TxnRespCode.REFUND_ERROR.getMsg());
                }
            } else {
                String mobile = secondLevel.getMobile();
                TlSmsLog smsLog = new TlSmsLog();
                smsLog.setTenantId(tenantId);
                smsLog.setCustomerId(customerId);
                smsLog.setChannel(loanMainInfo.getChannel());
                smsLog.setMobile(mobile);
                smsLog.setSmsType(TransBizConstant.SMS_TYPE_BIND_BANKCARD_REMIND);
                smsLog.setBusinessDate(businessDate);
                List<TlSmsLog> tlSmsLogs = extTlSmsLogMapper.querySmsLog(smsLog);
                // 一天只发送一次绑卡的提醒
                if (CollectionUtils.isEmpty(tlSmsLogs)) {
                    // 都没有的请款提示用户绑定一类卡并且返回退款失败
                    SendSmsReq req = new SendSmsReq();
                    logger.info("----绑卡提醒短信组装参数中----");
                    //生成流水号
                    req.setChannel(secondLevel.getChannel());
                    req.setTenantId(secondLevel.getTenantId());
                    // 身份证有效期短信
                    req.setSmsType(TransBizConstant.SMS_TYPE_BIND_BANKCARD_REMIND);
                    String smsSeqNo = (int) (Math.random() * 100000) + System.currentTimeMillis() + "";
                    req.setSeqNo(smsSeqNo);
                    req.setMobile(mobile);
                    req.setCustomerId(customerId);
                    try {
                        iSmsService.sendSmsMq(req);
                    } catch (Exception e) {
                        logger.error("绑卡提醒短信发送失败,用户：{}",customerId);
                        e.printStackTrace();
                        throw new TxnBizException(TxnRespCode.REFUND_ERROR.getMsg());
                    }
                    logger.info("绑卡提醒短信发送成功{}", customerId);
                }
                throw new TxnBizException(TxnRespCode.REFUND_ERROR.getMsg());
            }
        } else {
            refundLog.setToAcctNo(cardList.get(0).getCardNumber());
            refundLog.setToAcctName(customer.getFirstLevel().getChineseName());
            refundLog.setToAcctBranch(cardList.get(0).getBankId());
            refundLog.setToAcctBranchName(cardList.get(0).getBankName());
            refundLog.setInstdPty(cardList.get(0).getBankId());
        }


        refundLog.setPaymentSeqNo(seqNo);
        refundLog.setSumStatus(TransBizConstant.PAYMENT_LOG_SUM_STATUS_D); //最终状态 S 成功 F 失败  D 待发起
        refundLog.setRefNo(sequenceNumberService.generateRefNo(Constant.SEQUENCE_LENGTH));
        refundLog.setEffectDate(DateUtils.formatDate(new Date()));//生效日期	　	当前系统日期
        refundLog.setLaunchTime(DateUtils.getBusinessTime(new Date(), businessDate));//交易发起时间	　	当前时间
        refundLog.setUpdateBy(TransBizConstant.SYS_OPRATE);//操作员
        refundLog.setRecordVersionNumber(TransBizConstant.INIT_RECORD_VERSION_NUMBER);
        refundLog.setTransferTimes("0");
        refundLog.setGrayVersion(grayVersionConfig.getCurrentVersion());
        tlRefundLogMapper.insertSelective(refundLog);

        TlGlInterfaceInfo glInterfaceInfo = glInterfaceService.buildRefundGl(loanMainInfo, refundAmt, businessDate);
        extTlGlInterfaceInfoMapper.insertSelective(glInterfaceInfo);
        logger.info("溢缴款GL流水为:{}", BeanUtils.toMap(glInterfaceInfo));
    }


    /**
     * 冲销利，罚，复交易账户
     *
     * @param amLoanAccountInfo
     * @param refundAmt
     * @param businessDate
     * @param allRevert
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    public void revertAcct(AmLoanAccountInfo amLoanAccountInfo, BigDecimal refundAmt, Date businessDate, boolean allRevert) throws TxnException {
        String loanId = amLoanAccountInfo.getLoanId();
        String customerId = amLoanAccountInfo.getCustomerId();
        String tenantId = amLoanAccountInfo.getTenantId();

        List<String> typeList = Arrays.asList(TransBizConstant.ACCOUNT_TYPE_04, TransBizConstant.ACCOUNT_TYPE_11, TransBizConstant.ACCOUNT_TYPE_12);
        List<String> statusList = Arrays.asList(TransBizConstant.ACCOUNT_STATUS_1, TransBizConstant.ACCOUNT_STATUS_2, TransBizConstant.ACCOUNT_STATUS_8);
        List<AmAccountMainInfo> acctList = extAmAccountMainInfoMapper.selectAccounts(loanId, typeList, customerId, tenantId, statusList);
        if (CollectionUtils.isEmpty(acctList)) {
            return;
        }

        List<AmAccountMainInfo> unCloseAcctList = acctList.stream()
                .filter(orgi -> !TransBizConstant.ACCOUNT_STATUS_8.equals(orgi.getAccountStatus())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(unCloseAcctList)) {
            return;
        }

        List<String> accuTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10);
        List<String> accuStatusList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_STATUS_1, TransBizConstant.INTEREST_ACCRUAL_STATUS_8);
        List<AmInterestAccrualInfo> accuAcctList = extAmInterestAccrualInfoMapper.selectAllAccuAcct(loanId, customerId, tenantId, accuTypeList, accuStatusList);

        List<AmAccountMainInfo> updateAcctList = new ArrayList<>();
        List<TlGlInterfaceInfo> saveGlList = new ArrayList<>();
        AmLoanAccountInfo updateLoan = new AmLoanAccountInfo();
        updateLoan.setLoanId(amLoanAccountInfo.getLoanId());
        updateLoan.setTenantId(amLoanAccountInfo.getTenantId());
        updateLoan.setCustomerId(amLoanAccountInfo.getCustomerId());

        BigDecimal totalIntrRevertAmt = BigDecimal.ZERO;
        BigDecimal totalPentyRevertAmt = BigDecimal.ZERO;
        BigDecimal totalCompRevertAmt = BigDecimal.ZERO;

        for (AmAccountMainInfo acct : unCloseAcctList) {
            BigDecimal rate = acct.getActualDailyRate();
            AmAccountMainInfo updateAcct = new AmAccountMainInfo();
            updateAcct.setId(acct.getId());
            updateAcct.setLoanId(acct.getLoanId());
            updateAcct.setTenantId(acct.getTenantId());
            updateAcct.setCustomerId(acct.getCustomerId());
            BigDecimal revertAmt = BigDecimal.ZERO;

            if (allRevert) {
                revertAmt = acct.getCurrBalance();
            } else {
                AmInterestAccrualInfo revertAccuAcct = accuAcctList.stream().filter(accuAcct -> accuAcct.getAccountId().equals(acct.getOriginalAccountId())).findFirst().get();
                int days = revertAccuAcct.getAccrualTotalAccruDays();
                revertAmt = refundAmt.multiply(rate).multiply(BigDecimal.valueOf(days)).setScale(CommonContants.BIGDECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
                //最大冲余额
                if (revertAmt.compareTo(acct.getCurrBalance()) > 0) {
                    revertAmt = acct.getCurrBalance();
                }

                //不能小于0
                if (revertAmt.compareTo(BigDecimal.ZERO) < 0) {
                    revertAmt = BigDecimal.ZERO;
                }

            }

            updateAcct.setCreditAdjustAmount(acct.getCreditAdjustAmount().add(revertAmt));
            updateAcct.setCurrBalance(acct.getCurrBalance().subtract(revertAmt));
            //如果冲完则关闭账户
            if (updateAcct.getCurrBalance().compareTo(BigDecimal.ZERO) == 0) {
                updateAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            }

            if (revertAmt.compareTo(BigDecimal.ZERO) > 0) {
                updateAcctList.add(updateAcct);
                if (TransBizConstant.ACCOUNT_TYPE_04.equals(acct.getAccountType())) {
                    totalIntrRevertAmt = totalIntrRevertAmt.add(revertAmt);
                } else if (TransBizConstant.ACCOUNT_TYPE_11.equals(acct.getAccountType())) {
                    totalPentyRevertAmt = totalPentyRevertAmt.add(revertAmt);
                } else if (TransBizConstant.ACCOUNT_TYPE_12.equals(acct.getAccountType())) {
                    totalCompRevertAmt = totalCompRevertAmt.add(revertAmt);
                }
            }

            //生成调整gl流水
            String acctTxnCode = acct.getTxnCode();
            String txnCode = acctTxnCode.substring(0, 1) + (Integer.parseInt(acctTxnCode.substring(1)) + 1000);
            if (TransBizConstant.NON_ACCRUALS_FLA_1.equalsIgnoreCase(acct.getNonAccrualsFlag())) {
                txnCode = txnCode.replaceFirst("5", "9");
            }
            TlGlInterfaceInfo gl = glInterfaceService.buildTlGlInterfaceInfo(acct,
                    revertAmt, txnCode, acct.getAccountingPhase(), businessDate);
            if (gl != null) {
                saveGlList.add(gl);
            }
        }

        if (CollectionUtils.isNotEmpty(updateAcctList)) {
            extAmAccountMainInfoMapper.batchUpdateAccount(updateAcctList);
        }

        if (CollectionUtils.isNotEmpty(saveGlList)) {
            extTlGlInterfaceInfoMapper.batchInsertSelective(saveGlList);
        }

        if (totalIntrRevertAmt.compareTo(BigDecimal.ZERO) > 0
                || totalPentyRevertAmt.compareTo(BigDecimal.ZERO) > 0
                || totalCompRevertAmt.compareTo(BigDecimal.ZERO) > 0) {
            updateLoan.setCurrBalance(amLoanAccountInfo.getCurrBalance().subtract(totalIntrRevertAmt));
            updateLoan.setInterestBalance(amLoanAccountInfo.getInterestBalance().subtract(totalIntrRevertAmt));
            updateLoan.setPenaltyBalance(amLoanAccountInfo.getPenaltyBalance().subtract(totalPentyRevertAmt));
            updateLoan.setCompoundBalance(amLoanAccountInfo.getCompoundBalance().subtract(totalCompRevertAmt));
            extAmLoanAccountInfoMapper.updateByLoanId(updateLoan);
        }

    }

    /**
     * 冲销利，罚，复累计账户
     *
     * @param amLoanAccountInfo
     * @param refundAmt
     * @param businessDate
     * @param allRevert
     * @throws TxnException
     */
    @Transactional(rollbackFor = Exception.class)
    public void revertAccuAcct(AmLoanAccountInfo amLoanAccountInfo, BigDecimal refundAmt, Date businessDate, boolean allRevert) throws TxnException {
        String loanId = amLoanAccountInfo.getLoanId();
        String customerId = amLoanAccountInfo.getCustomerId();
        String tenantId = amLoanAccountInfo.getTenantId();

        List<String> accuTypeList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_TYPE_66, TransBizConstant.INTEREST_ACCRUAL_TYPE_10);
        List<String> statusList = Arrays.asList(TransBizConstant.INTEREST_ACCRUAL_STATUS_1, TransBizConstant.INTEREST_ACCRUAL_STATUS_8);
        List<AmInterestAccrualInfo> accuAcctList = extAmInterestAccrualInfoMapper.selectAllAccuAcct(loanId, customerId, tenantId, accuTypeList, statusList);
        if (CollectionUtils.isEmpty(accuAcctList)) {
            return;
        }

        List<AmInterestAccrualInfo> unCloseAccuAcctList = accuAcctList.stream()
                .filter(orgi -> !TransBizConstant.ACCOUNT_STATUS_8.equals(orgi.getAccountStatus())).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(unCloseAccuAcctList)) {
            return;
        }

        List<AmInterestAccrualInfo> updateAccuAcctList = new ArrayList<>();
        List<TlGlInterfaceInfo> saveGlList = new ArrayList<>();
        AmLoanAccountInfo updateLoan = new AmLoanAccountInfo();
        updateLoan.setLoanId(amLoanAccountInfo.getLoanId());
        updateLoan.setTenantId(amLoanAccountInfo.getTenantId());
        updateLoan.setCustomerId(amLoanAccountInfo.getCustomerId());
        BigDecimal totalIntrRevertAmt = BigDecimal.ZERO;
        BigDecimal totalPentyRevertAmt = BigDecimal.ZERO;
        BigDecimal totalCompRevertAmt = BigDecimal.ZERO;

        for (AmInterestAccrualInfo accuAcct : unCloseAccuAcctList) {
            BigDecimal rate = accuAcct.getAccrualDailyRate();
            AmInterestAccrualInfo updateAccuAcct = new AmInterestAccrualInfo();
            updateAccuAcct.setId(accuAcct.getId());
            updateAccuAcct.setLoanId(accuAcct.getLoanId());
            updateAccuAcct.setTenantId(accuAcct.getTenantId());
            updateAccuAcct.setCustomerId(accuAcct.getCustomerId());
            BigDecimal revertAmt = BigDecimal.ZERO;
            if (allRevert) {
                revertAmt = accuAcct.getAccrualTotalAccruInterest();
            } else {
                //按日计提，计算起息日到退款日的计提
                Date startDate = accuAcct.getAccrualStartDate();
                if (businessDate.compareTo(startDate) >= 0) {
                    int days = accuAcct.getAccrualTotalAccruDays();
                    revertAmt = refundAmt.multiply(rate).multiply(BigDecimal.valueOf(days)).setScale(CommonContants.BIGDECIMAL_SCALE, BigDecimal.ROUND_HALF_UP);
                }

                //最大冲累计值
                if (revertAmt.compareTo(accuAcct.getAccrualTotalAccruInterest()) > 0) {
                    revertAmt = accuAcct.getAccrualTotalAccruInterest();
                }

                //不能小于0
                if (revertAmt.compareTo(BigDecimal.ZERO) < 0) {
                    revertAmt = BigDecimal.ZERO;
                }
            }

            updateAccuAcct.setAccrualTotalAccruInterest(accuAcct.getAccrualTotalAccruInterest().subtract(revertAmt));
            //如果冲完则关闭账户
            if (updateAccuAcct.getAccrualTotalAccruInterest().compareTo(BigDecimal.ZERO) == 0) {
                updateAccuAcct.setAccountStatus(TransBizConstant.ACCOUNT_STATUS_8);
            }

            if (revertAmt.compareTo(BigDecimal.ZERO) > 0) {
                updateAccuAcctList.add(updateAccuAcct);
                if (TransBizConstant.INTEREST_ACCRUAL_TYPE_66.equals(accuAcct.getAccountType())) {
                    totalIntrRevertAmt = totalIntrRevertAmt.add(revertAmt);
                } else if (TransBizConstant.INTEREST_ACCRUAL_TYPE_10.equals(accuAcct.getAccountType())) {
                    totalPentyRevertAmt = totalPentyRevertAmt.add(revertAmt);
                } else if (TransBizConstant.INTEREST_ACCRUAL_TYPE_20.equals(accuAcct.getAccountType())) {
                    totalCompRevertAmt = totalCompRevertAmt.add(revertAmt);
                }
            }

            //生成调整gl流水
            String accuAccctTxnCode = accuAcct.getTxnCode();
            String txnCode = accuAccctTxnCode.substring(0, 1) + (Integer.parseInt(accuAccctTxnCode.substring(1)) + 1000);
            if (TransBizConstant.NON_ACCRUALS_FLA_1.equalsIgnoreCase(amLoanAccountInfo.getNonAccrualsFlag())) {
                txnCode = txnCode.replaceFirst("5", "9");
            }
            TlGlInterfaceInfo gl = glInterfaceService.buildTlGlInterfaceInfo(accuAcct,
                    revertAmt, txnCode, accuAcct.getAccountingPhase(), businessDate);
            if (gl != null) {
                saveGlList.add(gl);
            }
        }

        if (CollectionUtils.isNotEmpty(updateAccuAcctList)) {
            extAmInterestAccrualInfoMapper.batchUpdateInterestAccrualInfo(updateAccuAcctList);
        }

        if (CollectionUtils.isNotEmpty(saveGlList)) {
            extTlGlInterfaceInfoMapper.batchInsertSelective(saveGlList);
        }

        if (totalIntrRevertAmt.compareTo(BigDecimal.ZERO) > 0
                || totalPentyRevertAmt.compareTo(BigDecimal.ZERO) > 0
                || totalCompRevertAmt.compareTo(BigDecimal.ZERO) > 0) {
            updateLoan.setAccruedInterest(amLoanAccountInfo.getAccruedInterest().subtract(totalIntrRevertAmt));
            updateLoan.setAccruedPenalty(amLoanAccountInfo.getAccruedPenalty().subtract(totalPentyRevertAmt));
            updateLoan.setCompoundBalance(amLoanAccountInfo.getAccruedCompound().subtract(totalCompRevertAmt));
            extAmLoanAccountInfoMapper.updateByLoanId(updateLoan);
        }
    }

}
